/*
 ** Authored by Timothy Gerard Endres
 ** <mailto:time@gjt.org>  <http://www.trustice.com>
 ** This work has been placed into the public domain.
 ** You may use this work in any way and for any purpose you wish.
 ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
 ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
 ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
 ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
 ** REDISTRIBUTION OF THIS SOFTWARE. 
 */
package be.djdb.externstuff.tuned;
/**
 * This class encapsulates the Tar Entry Header used in Tar Archives. The class
 * also holds a number of tar constants, used mostly in headers.
 * @author Timothy Gerard Endres, <time@gjt.org>
 * @coderefactor 4plus
 */
public class TarHeader extends Object implements Cloneable {
	public static final int NAMELEN = 100;//The length of the name field in a header buffer.
	public static final int NAMEOFFSET = 0;//The offset of the name field in a header buffer.
	public static final int PREFIXLEN = 155;//The length of the name prefix field in a header buffer.
	public static final int PREFIXOFFSET = 345;//The offset of the name prefix field in a header buffer.
	public static final int MODELEN = 8;//The length of the mode field in a header buffer.
	public static final int UIDLEN = 8;//The length of the user id field in a header buffer.
	public static final int GIDLEN = 8;//The length of the group id field in a header buffer.
	public static final int CHKSUMLEN = 8;//The length of the checksum field in a header buffer.
	public static final int SIZELEN = 12;//The length of the size field in a header buffer.
	public static final int MAGICLEN = 8;//The length of the magic field in a header buffer.
	public static final int MODTIMELEN = 12;//The length of the modification time field in a header buffer.
	public static final int UNAMELEN = 32;//The length of the user name field in a header buffer.
	public static final int GNAMELEN = 32;//The length of the group name field in a header buffer.
	public static final int DEVLEN = 8;//The length of the devices field in a header buffer.
	/**
	 * LF_ constants represent the "link flag" of an entry, or more commonly,
	 * the "entry type". This is the "old way" of indicating a normal file.
	 */
	public static final byte LF_OLDNORM = 0;
	public static final byte LF_NORMAL = (byte) '0';// Normal file type.
	public static final byte LF_LINK = (byte) '1';//Link file type.
	public static final byte LF_SYMLINK = (byte) '2';//Symbolic link file type.
	public static final byte LF_CHR = (byte) '3';//Character device file type.
	public static final byte LF_BLK = (byte) '4';//Block device file type.
	public static final byte LF_DIR = (byte) '5';//Directory file type.
	public static final byte LF_FIFO = (byte) '6';//FIFO (pipe) file type.
	public static final byte LF_CONTIG = (byte) '7';//Contiguous file type
	public static final String TMAGIC = "ustar";//The magic tag representing a POSIX tar archive.
	public static final String GNU_TMAGIC = "ustar  ";//The magic tag representing a GNU tar archive.
	public StringBuffer name;//The entry's name.
	public int mode;//The entry's permission mode.
	public int userId;//The entry's user id.
	public int groupId;//The entry's group id.
	public long size;//The entry's size.
	public long modTime;//The entry's modification time.
	public int checkSum;//The entry's checksum.
	public byte linkFlag;//The entry's link flag.
	public StringBuffer linkName;//The entry's link name.
	public StringBuffer magic;//The entry's magic tag.
	public StringBuffer userName;//The entry's user name.
	public StringBuffer groupName;//The entry's group name
	public int devMajor;//The entry's major device number.
	public int devMinor;//The entry's minor device number.
	public TarHeader() {
		this.magic = new StringBuffer(TarHeader.TMAGIC);
		this.name = new StringBuffer();
		this.linkName = new StringBuffer();
		String user = System.getProperty("user.name", "");
		if (user.length() > 31)
			user = user.substring(0, 31);
		this.userId = 0;
		this.groupId = 0;
		this.userName = new StringBuffer(user);
		this.groupName = new StringBuffer("");
	}
	/**
	 * TarHeaders can be cloned.
	 */
	public Object clone() {
		TarHeader hdr = null;
		try {
			hdr = (TarHeader) super.clone();
			hdr.name = (this.name == null) ? null : new StringBuffer(this.name.toString());
			hdr.mode = this.mode;
			hdr.userId = this.userId;
			hdr.groupId = this.groupId;
			hdr.size = this.size;
			hdr.modTime = this.modTime;
			hdr.checkSum = this.checkSum;
			hdr.linkFlag = this.linkFlag;
			hdr.linkName = (this.linkName == null) ? null : new StringBuffer(this.linkName.toString());
			hdr.magic = (this.magic == null) ? null : new StringBuffer(this.magic.toString());
			hdr.userName = (this.userName == null) ? null : new StringBuffer(this.userName.toString());
			hdr.groupName = (this.groupName == null) ? null : new StringBuffer(this.groupName.toString());
			hdr.devMajor = this.devMajor;
			hdr.devMinor = this.devMinor;
		} catch (CloneNotSupportedException ex) {
			ex.printStackTrace(System.err);
		}
		return hdr;
	}
	/**
	 * Get the name of this entry.
	 * @return Teh entry's name.
	 */
	public String getName() {
		return this.name.toString();
	}
	/**
	 * Parse an octal string from a header buffer. This is used for the file
	 * permission mode value.
	 * @param header    The header buffer from which to parse.
	 * @param offset    The offset into the buffer from which to parse.
	 * @param length    The number of header bytes to parse.
	 * @return The long value of the octal string.
	 */
	public static long parseOctal(byte[] header, int offset, int length)	throws InvalidHeaderException {
		long result = 0;
		boolean stillPadding = true;
		int end = offset + length;
		for (int i = offset; i < end; ++i) {
			if (header[i] == 0)	break;
			if (header[i] == (byte) ' ' || header[i] == '0') {
				if (stillPadding)continue;
				if (header[i] == (byte) ' ')break;
			}
			stillPadding = false;
			result = (result << 3) + (header[i] - '0');
		}
		return result;
	}
	/**
	 * Parse a file name from a header buffer. This is different from
	 * parseName() in that is recognizes 'ustar' names and will handle adding on
	 * the "prefix" field to the name.
	 * Contributed by Dmitri Tikhonov <dxt2431@yahoo.com>
	 * @param header      The header buffer from which to parse.
	 * @param offset      The offset into the buffer from which to parse.
	 * @param length      The number of header bytes to parse.
	 * @return The header's entry name.
	 */
	public static StringBuffer parseFileName(byte[] header) {
		StringBuffer result = new StringBuffer(256);
		// If header[345] is not equal to zero, then it is the "prefix"
		// that 'ustar' defines. It must be prepended to the "normal"
		// name field. We are responsible for the separating '/'.
		//
		if (header[345] != 0) {
			for (int i = 345; i < 500 && header[i] != 0; ++i) {
				result.append((char) header[i]);
			}
			result.append("/");
		}
		for (int i = 0; i < 100 && header[i] != 0; ++i) {
			result.append((char) header[i]);
		}
		return result;
	}
	/**
	 * Parse an entry name from a header buffer.
	 * @param header      The header buffer from which to parse.
	 * @param offset      The offset into the buffer from which to parse.
	 * @param length      The number of header bytes to parse.
	 * @return The header's entry name.
	 */
	public static StringBuffer parseName(byte[] header, int offset, int length)	throws InvalidHeaderException {
		StringBuffer result = new StringBuffer(length);
		int end = offset + length;
		for (int i = offset; i < end; ++i) {
			if (header[i] == 0)
				break;
			result.append((char) header[i]);
		}
		return result;
	}
	/**
	 * This method, like getNameBytes(), is intended to place a name into a
	 * TarHeader's buffer. However, this method is sophisticated enough to
	 * recognize long names (name.length() > NAMELEN). In these cases, the
	 * method will break the name into a prefix and suffix and place the name in
	 * the header in 'ustar' format. It is up to the TarEntry to manage the
	 * "entry header format". This method assumes the name is valid for the type
	 * of archive being generated.
	 * @param outbuf        The buffer containing the entry header to modify.
	 * @param newName       The new name to place into the header buffer.
	 * @return The current offset in the tar header (always TarHeader.NAMELEN).
	 * @throws InvalidHeaderException
	 *             If the name will not fit in the header.
	 */
	public static int getFileNameBytes(String newName, byte[] outbuf)	throws InvalidHeaderException {
		if (newName.length() > 100) {
			// Locate a pathname "break" prior to the maximum name length...
			int index = newName.indexOf("/", newName.length() - 100);
			if (index == -1)
				throw new InvalidHeaderException("file name is greater than 100 characters, " + newName);
			// Get the "suffix subpath" of the name.
			String name = newName.substring(index + 1);
			// Get the "prefix subpath", or "prefix", of the name.
			String prefix = newName.substring(0, index);
			if (prefix.length() > TarHeader.PREFIXLEN)
				throw new InvalidHeaderException("file prefix is greater than 155 characters");
			TarHeader.getNameBytes(new StringBuffer(name), outbuf,TarHeader.NAMEOFFSET, TarHeader.NAMELEN);
			TarHeader.getNameBytes(new StringBuffer(prefix), outbuf,TarHeader.PREFIXOFFSET, TarHeader.PREFIXLEN);
		} else {
			TarHeader.getNameBytes(new StringBuffer(newName), outbuf,TarHeader.NAMEOFFSET, TarHeader.NAMELEN);
		}
		// The offset, regardless of the format, is now the end of the
		// original name field.
		return TarHeader.NAMELEN;
	}
	/**
	 * Move the bytes from the name StringBuffer into the header's buffer.
	 * @param header      The header buffer into which to copy the name.
	 * @param offset      The offset into the buffer at which to store.
	 * @param length      The number of header bytes to store.
	 * @return The new offset (offset + length).
	 */
	public static int getNameBytes(StringBuffer name, byte[] buf, int offset,int length) {
		int i;
		for (i = 0; i < length && i < name.length(); ++i) {
			buf[offset + i] = (byte) name.charAt(i);
		}
		for (; i < length; ++i) {
			buf[offset + i] = 0;
		}
		return offset + length;
	}
	/**
	 * Parse an octal integer from a header buffer.
	 * @param header		  The header buffer from which to parse.
	 * @param offset          The offset into the buffer from which to parse.
	 * @param length          The number of header bytes to parse.
	 * @return The integer value of the octal bytes.
	 */
	public static int getOctalBytes(long value, byte[] buf, int offset,	int length) {
		byte[] result = new byte[length];
		int idx = length - 1;
		buf[offset + idx] = 0;
		--idx;
		buf[offset + idx] = (byte) ' ';
		--idx;
		if (value == 0) {
			buf[offset + idx] = (byte) '0';
			--idx;
		} else {
			for (long val = value; idx >= 0 && val > 0; --idx) {
				buf[offset + idx] = (byte) ((byte) '0' + (byte) (val & 7));
				val = val >> 3;
			}
		}
		for (; idx >= 0; --idx) {
			buf[offset + idx] = (byte) ' ';
		}
		return offset + length;
	}
	/**
	 * Parse an octal long integer from a header buffer.
	 * @param header       The header buffer from which to parse.
	 * @param offset       The offset into the buffer from which to parse.
	 * @param length       The number of header bytes to parse.
	 * @return The long value of the octal bytes.
	 */
	public static int getLongOctalBytes(long value, byte[] buf, int offset,	int length) {
		byte[] temp = new byte[length + 1];
		TarHeader.getOctalBytes(value, temp, 0, length + 1);
		System.arraycopy(temp, 0, buf, offset, length);
		return offset + length;
	}
	/**
	 * Parse the checksum octal integer from a header buffer.
	 * @param header       The header buffer from which to parse.
	 * @param offset       The offset into the buffer from which to parse.
	 * @param length       The number of header bytes to parse.
	 * @return The integer value of the entry's checksum.
	 */
	public static int getCheckSumOctalBytes(long value, byte[] buf, int offset,	int length) {
		TarHeader.getOctalBytes(value, buf, offset, length);
		buf[offset + length - 1] = (byte) ' ';
		buf[offset + length - 2] = 0;
		return offset + length;
	}
}// no crimalcode Tuned by Lieven
